查看原文
其他

在前端性能优化中应用HTTP缓存的三部曲|洞见

2017-02-23 王岩 思特沃克

Spike先生是Best Experience公司的IT运营部门主管,他的团队成功地利用Http Cache优化了前端工程。

Spike将通过三个Scenario来展示他的团队是如何做到这一点的:

  • 通过配置Http Cache Expire来消减访问压力,提高用户体验

  • 通过版本化来强制失效本地的过期缓存

  • 通过内容摘要命名文件来更精确的控制缓存以及实现非覆盖式的发布


第一个故事:我不想要那么多服务器和带宽

Best Experience面临的资源访问压力和用户体验方面的问题

随着Best Experience提供的前端应用越来越强大,Spike的压力也越来越大:

  • IT部门为了应对来自静态资源的访问压力,不断购置服务器和带宽。

  • 糟糕的用户体验使得用户转投到竞争对手的网站。

工程师们刚刚通过应用Minify、AMD、打包、Gzip等手段优化了前端页面的体验, 最终得到如下图所示的一个资源引用关系:

“还是很多东西要下载啊,该拿什么来拯救该死的延迟呢?”——Spike看着图想到。

他突然想起来:在早年间,Yahoo曾发布了《关于优化前端体验的35条建议和指导》,其中第三条是:“Add an Expires or a Cache-Control Header”

Yahoo是这样描述这条建议的:

Web page designs are getting richer and richer, which means more scripts, stylesheets, images, and Flash in the page. A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.

Browsers (and proxies) use a cache to reduce the number and size of HTTP requests, making web pages load faster.

“这个正是我寻找的银弹”——Spike得意的笑了。

于是,Spike写下了第一个Technology Story

作为IT 部门的老大:

我希望通过应用HTTP缓存技术,重用已经下载过的资源,

用于消减用户在浏览页面时产生的不必要的Http Request。

以此,来提升用户在浏览页面时候的体验,

以及降低对于公司服务器资源的访问压力。

并找来了工程师Tom。

Expire带来的美好生活

Tom刚刚参与了前一轮的优化工作,虽然成果显著,但是他并不满足。

当Tom看到Jim写下的Story时眼前一亮:“这个方法太赞了!我甚至可以在登录页面底部放置对其他页面资源的引用。提升用户在整个网站的浏览体验。”——Tom的小宇宙瞬间爆发,很快就完成了新的优化方案。

Best-Experience的用户在接下来的时间里浏览页面,会这样下载资源,以图片bgimage.png为例:

  • 用户第一次获取图片的时候,Http Request 如图:

  • 之后用户再次获取图片的时候,则完全可以从浏览器的缓存中读取数据了。

因为采用了Http缓存方案,

  • 用户的feedback越来越好,访问量提高了;

  • IT部门也不用那么多服务器和带宽了。

财务总监邀请Spike共进晚餐,并谈起了自己在希腊的度假。

“我想我也应该去圣托里尼度个假,犒劳下自己”——Spike美滋滋的想到。


第二个故事:失效缓存是个技术活

这个BUG我们明明修了啊!

一天,QA Tyke发现最近一轮发布的前端应用中没有包含很多新的feature。Jerry承诺说已经跟着这个月的release上线了,还测试过了。经过一番折腾,Jerry发现浏览器一直在使用旧的缓存,而不是最新的版本。Spike找来了Jerry 和Tom,三个人一起手动对引用的资源做了重命名、做了紧急修复。

“真是没有银弹啊,我的圣托里尼啊!”——Spike头疼的想到。

Spike、Jerry、Tom和Tyke坐在了一起,得出了新的结论:

  • 缓存前端工程中的资源时,需要考虑缓存有效期的问题

  • 虽然35条建议和指导中建议“Configure ETags”,但是很难确定静态资源缓存的有效期

  • 虽然Http缓存可以支持No-Cache或者max-age =0的方式,保证浏览器每次都向服务器验证缓存有效性,但是这样会大大增加服务器的压力

  • 可以通过在资源引用上增加形如:<.... src="###.js?v=$version$">的版本化方式,来强制浏览器更新缓存。

Spike写下了新的Technology Story

作为IT部门的老大:

我希望在前端系统中,对引用的静态资源进行版本化管理。

使之既可以通过Http缓存来提升用户体验,降低服务器压力;

也可以方便用户即时获得更新后的资源。

“这都10月了,看来是去不成圣托里尼了,总觉得这个方案哪里有问题”——Spike忐忑不安。

用版本机制来保证浏览器更新资源

Jerry和Tom(很难想象他们两怎么配合的)终于在前端工程中实现了自动化的资源版本化管理:用户在最初访问页面的时候,会得到这样一个资源引用:

而当新的版本上线后,用户会得到这样一个资源引用:


第三个故事:更精确的缓存管理和平滑升级

(这个案例来自于知乎的大公司里怎样开发和部署前端代码? 张云龙的回答,前一个 story的内容有涉及)

每次更新后的尖峰时刻

11月的Release后,运维人员Nibbles找到Spike,“这次上线以后,服务器压力突然剧增,从GA上看到用户花了很多时间在资源下载上”,Spike找来了Tom、Jerry、Tyke和Nibbles,几个人坐在一起分析原因:

“这是因为11月的部署完成后,前端应用引用的资源版本升级,所有缓存失效导致的”——Tom 想了想说

“所有的资源引用?我还以为我们能精确到每一个文件的更新呢”——Nibbles惊讶道

“如果单独标明每一个资源的版本,那么按照我们的实际情况来看,每次上线后访问压力就没那么大了”——Tyke

“我之前看WebPack做到了”——Jerry兴致勃勃的谈了起来。

“他们采用的是文件摘要的方式,就是用MD5对文件求值,如果两个文件是相同的,那么就求得同一个hash值;如果文件是不同的,就求得不同的hash值”——Jerry

“我们可以用这些文件的hash值作为版本号,就像这样”——Jerry

“能不能通过文件名做版本管理,我希望知道哪些文件是这次部署要移除的,哪些是新增的”——Nibbles

“这有什么问题么?”——Spike很疑惑

“明年不是要做CDN么?静态资源和页面文件会放置到不同的服务器上,很难做到页面文件和静态资源同批次更新,而且CDN的资源生效是有延迟的”——Nibbles

(关于 CDN 和非覆盖部署式部署,请参考张云龙的大公司里怎样开发和部署前端代码?和前端工程之CDN部署)

"恩,那么就这样吧,我回去写Story。"——Spike 一锤定音。

"还好,我们之前用了WebPack,这就简单了"——Jerry

Spike写下了第三个Story

作为IT 部门的老大:

我希望能用文件hash来命名静态资源文件,

使之可以按照文件来控制缓存和部署

"我觉得这回是最后一个Story了"——Spike越来越乐观。

过渡到非覆盖式部署——大圆满?

如何应用WebPack的具体过程不再概述。

图片来源大公司里怎样开发和部署前端代码?

这样,Nibbles就可以很愉快的通过文件名比对,来分析每次部署变更的内容;而Best Experience未来上线的流程也会变为:

  • 先将新增的静态资源文件发布到静态资源服务器上

  • 验证新的静态资源是否正确发布

  • 服务器暂时离线,替换 html 文件等

  • 删除无用的静态资源文件

“终于可以踏踏实实过圣诞节了”——Spike看着日历。


总结

Spike的总结

年底了,Spike在年终总结中写到:

以后在实施前端工程中,我们可以通过:

  • 配置永不过期的本地缓存——节约带宽,提升用户体验

  • 采用文件摘要作为缓存依据——更精确的缓存控制

  • 采用CDN——降低用户请求资源时解析DNS的延迟

  • 利用文件摘要作为文件名——实现非覆盖式的部署,降低down time

我的总结

我引用前端工程之CDN部署一文中对非覆盖式、缓存设计、CDN这些解决方案间的前因后果做的总结:

如果考虑到项目开发阶段,那么这将是更为复杂的软件工程问题。在这个问题域中,还需要囊括文件压缩、合并、打包、重命名、目录设置等问题。还好Gulp、Webpack、FIS、AMD、RequireJS这些工具及对应的插件能帮助到我们。WebPack提供了Hash、ChunkHash、ContentHash,与此同时,社区提供了MD5-Hash。

当然这些都是关于工具的话题了,这次我们主要谈的是工程。浅谈前端集成解决方案里提到了前端领域的8个技术元素与分类,挺有意思的。


再终——没有银弹

我们的Spike先生来到了2月的北京旅游,放个带色的图:

(我们自强不吸!)

在机场,Spike还是接到了Tyke的电话,“老爹啊,WebPack那个文件摘要不准啊........”

“您好,因为天气原因,去往####的飞机延误,请您耐心等候........”

“.......”


- 相关阅读 -

4月14日-4月15日WOTA全球架构与运维技术峰会

前端组件化开发方案及其在React Native中的应用|洞见

点击[阅读原文]可至官网查看原文内容。

本文版权属ThoughtWorks公司所有,如需转载请在后台留言联系。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存